package client;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import java.net.InetAddress;
import java.net.Socket;

import client.GUI.GUIfuncs;
import client.GUI.Panel;

import server.Message;
import server.utils.CommandType;
import server.utils.Configurations;
import server.utils.Log;


public class Client extends Thread{
	public static Log log;
	public static Configurations conf;
	private Socket socket;
	private ObjectOutputStream netOut;
	private ObjectInputStream netIn;
	/**
	 * Graphical panel where table and players are.
	 */
	private Panel p;
	/**
	 * Helpclass for executing GUI functions.
	 */
	private GUIfuncs panel;
	/**
	 * Client intialization.
	 * NB! Also must Client.authorize()!
	 * @param name Player's name
	 * @param serverName Server's name
	 * @param port Server port
	 * @param panel Mainpanel
	 */
	public Client(Panel p) {
		this.p = p;
		this.panel = p.getGuiFuncs();
		log = new Log("log.txt", "conf.txt");
		try {
			conf = new Configurations(log.readConf());
		} catch (Exception e) {
			log.println("Insufficient configurations! Client ended!", true);
			System.exit(1);
		}
	}
	/**
	 * Connect to server.
	 * @param serverName Server's name.
	 * @param port Port.
	 */
	public void connect(String serverName, int port) {
		try {
			InetAddress serverAddr = InetAddress.getByName(serverName);
			log.println("Connecting to server " + serverAddr.getHostName(),
					conf.isConsole());
			socket = new Socket(serverAddr, port);
			netIn = new ObjectInputStream(socket.getInputStream());
			netOut = new ObjectOutputStream(socket.getOutputStream());
			log.println("Connected!", conf.isConsole());
		} catch (IOException e) {
			log.println("Unable to connect to server " + serverName, conf.isConsole());
			System.exit(1);
		}
	}
	@Override
	public void run() {
		try {
			while(true) {
				Object fromServer = netIn.readObject();
				if (fromServer != null) {
					Message msg = (Message) fromServer;
					try{
						log.println(msg.toString(), conf.isConsole());
					} catch(NullPointerException e) {
						System.out.println("Error @ Client.run() logi writing");
					}
					switch(msg.getType()) {
					case PLAYERS:
						panel.setOpponents(msg.getPlayers());
						break;
					case EDITPLAYER:
						panel.editOpponent(msg.getFrom());
						break;
					case TURNCHANGE:
						boolean turn;
						if (msg.getCommandValue().equalsIgnoreCase("true")) {
							turn = true;
						} else {
							turn = false;
						}
						p.setTurn(turn);
						sendMessage(new Message(this.getName(), conf.getServerName(),
								CommandType.OK, "Turnchange ok"));
						break;
					case SENDCARD:
						if (msg.getFrom().equalsIgnoreCase(conf.getServerName())) {
							panel.addCardToPlayer(msg.getTo(), msg.getCard());
							sendMessage(new Message(this.getName(), conf.getServerName(),
									CommandType.OK, "Card recieved"));
						} else {
							panel.addCardToPlayer(msg.getFrom(), msg.getCard());
						}
						break;
					case DEALTABLE:
						panel.dealCardToTable(msg.getCard());
						sendMessage(new Message(this.getName(), conf.getServerName(),
								CommandType.OK, "(Table)Card recieved"));
						break;
					case DEALMONEY:
						if (msg.getFrom().equalsIgnoreCase(
								Client.conf.getServerName())) {
							panel.dealMoney(Double.parseDouble(msg.getCommandValue()));
							sendMessage(new Message(this.getName(), 
									conf.getServerName(),
									CommandType.OK, "Money recieved"));
						} else {
							panel.dealOpponentMoney(msg.getFrom(), 
									Double.parseDouble(msg.getCommandValue()));
						}
						panel.chatInfo(msg);
						break;
					case BET:
						panel.opponentBet(msg.getFrom(),
								Double.parseDouble(msg.getCommandValue()));
						panel.chatInfo(msg);
						break;
					case ENDOFROUND:
						panel.chat(msg);
						break;
					case CHAT:
						panel.chat(msg);
						break;
					case SENDALLDATA:
						panel.dataSync(msg);
						break;
					}
				}
			}
		} catch (IOException e) {
			log.println("IOException @ Client.run()", conf.isConsole());
			disconnect();
			p.getWindow().dispose();
		} catch (ClassNotFoundException e) {
			log.println("ClassNotFoundException @ Client.run()", conf.isConsole());
		}
	}
	/**
	 * Method for sending messages to server.
	 * Message is an object.
	 * @param msg Message to send
	 */
	public synchronized void sendMessage(Message msg) {
		try {
			if (!socket.isClosed()) {
				netOut.writeObject(msg);
			} else {
				throw new IOException(); 			// tegelikult: CALL catch()
			}
		} catch (IOException eee) {
			log.println("IOException @ Client.sendMessage(). Closing socket!",
					conf.isConsole());
			disconnect();
		}
	}
	/**
	 * Authorize client @ server.
	 * Also Client.start()
	 * @return True if client was authorized.
	 * 		   False if duplicate name existed in server or error
	 * 		   occurred.
	 */
	public synchronized boolean authorize(String name) {
		try {
			Message toServer = new Message(name, conf.getServerName(),
					CommandType.INFO, name);
			this.sendMessage(toServer);
			Object fromServer = netIn.readObject();
			if (fromServer != null) {
				Message msg = (Message) fromServer;
				if (msg.getType().equals(CommandType.ERROR)) {
					return false;
				} else if (msg.getType().equals(CommandType.OK)){
					log.println(msg.toString(), conf.isConsole());
					this.setName(name);
					return true;
				}
			}
		} catch (IOException e) {
			log.println("IOException @ Client.authorize()", conf.isConsole());
		} catch (ClassNotFoundException ee) {
			log.println("ClassNotFoundException @ Client.authorize()", conf.isConsole());
		}
		return false;
	}
	/**
	 * Disconnect client from server.
	 */
	public void disconnect() {
		try {
			netOut.writeObject(new Message(this.getName(), conf.getServerName(),
					CommandType.QUIT, "Disconnecting!"));
			socket.close();
		} catch (IOException e) {
			log.println("Error @ closing socket.", conf.isConsole());
		}
	}
}
